package com.chukun.dubbo.spi;

import com.chukun.dubbo.spi.service.Order;
import com.chukun.dubbo.spi.service.Order$Adaptive;
import com.chukun.dubbo.spi.service.impl.AdaptiveOrder;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;

import java.util.List;

/**
 * @author chukun
 * @version 1.0.0
 * @description dubbo spi测试
 * @createTime 2021年12月26日 19:11:00
 *
 */
public class DubboSpiRunner {

    public static void main(String[] args) {
        // 简单的spi的使用
        spiAction();
        // adpative的基本使用
        //adaptiveMethodAction01();
        // adpative修饰类的基本使用
        //adaptiveClassAction("alipay");

        // wrapper机制的基本使用
        //wrapperAction();

        // activate机制的基本使用
        // 一次性激活（加载）一批扩展类实例，这里指定的是激活所有group为“online"的扩展类
        //activateAction("", "online");

        // 一次性激活（加载）一批扩展类实例，这里指定的是激活所有group为“offline"的扩展类
        // activateAction("","offline");

        // 根据@Activate的注解的value，激活对应的接口实现类
        activateAction02("order", "online");
    }

    /**
     * spi使用示例
     *
     * Dubbo 并没有直接使用 JDK 的 SPI，而是在其基础之上对其进行了改进
     * Dubbo 的 SPI 规范是：
     * 接口名：可以随意定义， 但接口必须被@SPI 注解修饰
     * 实现类名：在接口名前添加一个用于表示自身功能的“标识前辍”字符串
     * 提供者配置文件路径：在依次查找的目录为
     *    META-INF/dubbo/internal
     *    META-INF/dubbo
     *    META-INF/services
     * 提供者配置文件名称：接口的全限定性类名，无需扩展名
     * 提供者配置文件内容：文件的内容为 key=value 形式， value 为该接口的实现类的全限类性类名， key 可以随意，但一般为该实现类的“标识前辍”（首字母小写）。一个类名占一行。
     * 提供者加载： ExtensionLoader 类相当于 JDK SPI 中的 ServiceLoader 类，用于加载提供者配置文件中所有的实现类，并创建相应的实例。
     */
    private static void spiAction() {
        // 获取到用于加载Order类型扩展类实例的extensionLoader实例
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        Order alipay = loader.getExtension("alipay");
        System.out.println(alipay.way());

        Order wechat = loader.getExtension("wechat");
        System.out.println(wechat.way());
    }

    /**
     * 从前面的动态生成的 Adaptive 类中的 adaptiveMethod()方法体可知， 其对于要加载的扩
     * 展名的指定方式是通过 URL 类型的方法参数指定的。所以对于 Adaptive 方法的定义规范仅
     * 一条：其参数包含 URL 类型的参数，或参数可以获取到 URL 类型的值。方法调用者是通过
     * URL 传递要加载的扩展名的
     *
     * @see Order$Adaptive
     */
    private static void adaptiveMethodAction01() {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        // 获取Order的自适应类，不是任何一个Order的实现类
        Order order = loader.getAdaptiveExtension();

        URL url = URL.valueOf("dubbo://localhost:8080/adaptive");
        System.out.println(order.pay(url));
    }

    /**
     * 通过url参数指定，调用对应的接口实现类
     * @see Order$Adaptive
     */
    private static void adaptiveMethodAction02() {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        // 获取Order的自适应类，不是任何一个Order的实现类
        Order order = loader.getAdaptiveExtension();
        URL url = URL.valueOf("dubbo://localhost:8080/adaptive?order=alipay");
        System.out.println(order.pay(url));
    }

    /**
     *
     */
    private static void adaptiveClassAction(String orderName) {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        Order order = loader.getAdaptiveExtension();
        ((AdaptiveOrder)order).setDefaultName(orderName);
        System.out.println(order.way());
    }


    /**
     * Wrapper 机制，即扩展类的包装机制。就是对扩展类中的 SPI 接口方法进行增强，进行
     * 包装，是 AOP 思想的体现，是 Wrapper 设计模式的应用。一个 SPI 可以包含多个 Wrapper
     *
     * Wrapper 机制不是通过注解实现的，而是通过一套 Wrapper 规范实现的。
     * Wrapper 类在定义时需要遵循如下规范。
     *   该类要实现 SPI 接口
     *   该类中要有 SPI 接口的引用
     *   该类中 SPI 接口实例是通过仅包含一个 SPI 接口参数的带参构造器传的
     *   在接口实现方法中要调用 SPI 接口引用对象的相应方法
     *   该类名称以 Wrapper 结尾
     */
    private static void wrapperAction() {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        Order order = loader.getAdaptiveExtension();
        URL url = URL.valueOf("dubbo://localhost:8080/wrapper?order=alipay");
        System.out.println(order.pay(url));
    }

    /**
     * 激活机制 Activate
     *   用于激活扩展类的。
     *   Activate 机制，即扩展类的激活机制。通过指定的条件来激活当前的扩展类。其是通过@Active 注解实现的。
     *
     * 对于@Activate 注解
     *  在@Activate 注解中共有五个属性，其中 before、 after 两个属性已经过时，剩余有效属性还有三个。它们的意义为：
     *     group： 为扩展类指定所属的组别，是当前扩展类的一个标识。 String[]类型，表示一个扩展类可以属于多个组。
     *     value： 为当前扩展类指定的 key，是当前扩展类的一个标识。 String[]类型，表示一个扩展类可以有多个指定的 key。
     *     order： 指定筛选条件相同的扩展类的加载顺序。序号越小，优先级越高。默认值为 0。
     *
     * @param key 可以使用url指定
     */
    private static void activateAction(String key, String group) {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        URL url = URL.valueOf("dubbo://localhost:8080/activate?order=alipay");

        // 一次性激活（加载）一批扩展类实例，这里指定的是激活所有group为“online"的扩展类
        List<Order> orders = loader.getActivateExtension(url, key, group);
        for (Order order : orders) {
            System.out.println(order.way());
        }
    }

    /**
     * 根据key激活相关的接口实现类
     * @param key url的参数键
     * @param group
     */
    private static void activateAction02(String key, String group) {
        ExtensionLoader<Order> loader = ExtensionLoader.getExtensionLoader(Order.class);
        URL url = URL.valueOf("dubbo://localhost:8080/activate?order=alipay");

        //// getActivateExtension()的参数二与三的关系是“或”
        //      参数二指定的是要激活的value
        //      参数三指定的是要激活的group
        List<Order> orders = loader.getActivateExtension(url, key, group);
        for (Order order : orders) {
            System.out.println(order.way());
        }
    }

}
